package com.nervenets.web.hibernate.dao;

import com.nervenets.web.enumeration.PropertyName;
import com.nervenets.web.enumeration.WealthType;
import com.nervenets.web.hibernate.BaseDao;
import com.nervenets.web.hibernate.DomainObject;
import com.nervenets.web.hibernate.domain.Property;
import com.nervenets.web.hibernate.domain.user.Bank;
import com.nervenets.web.hibernate.domain.user.User;
import com.nervenets.web.hibernate.domain.user.WealthFlow;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.query.Query;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Joe on 2018/1/5.
 */
@Component
public class UserDao extends BaseDao {

    public User getUserByMobile(String mobile) {
        return getHibernateTemplate().execute(session -> {
            Query query = session.createQuery("from User where mobile = :mobile");
            query.setParameter("mobile", mobile);
            return (User) query.uniqueResult();
        });
    }

    public User getUserByCode(String code) {
        return getHibernateTemplate().execute(session -> {
            Query query = session.createQuery("from User where code = :code");
            query.setParameter("code", code);
            return (User) query.uniqueResult();
        });
    }

    public User getUserByWechatId(String wechatId) {
        return getHibernateTemplate().execute(session -> {
            Query query = session.createQuery("from User where wechatId = :wechatId");
            query.setParameter("wechatId", wechatId);
            return (User) query.uniqueResult();
        });
    }

    public Property getProperty(final PropertyName propertyName, final boolean cacheable) {
        return getHibernateTemplate().execute(session -> {
            Query query = session.createQuery("from Property p where p.name = :name");
            query.setParameter("name", propertyName);
            query.setCacheable(cacheable);
            final List list = query.list();
            if (list.isEmpty()) {
                return new Property(propertyName);
            }
            return (Property) list.get(0);
        });
    }

    public long getSurplus(long userId, WealthType wealthType) {
        try {
            Long result = getJdbcTemplate().queryForObject("select surplus from user_wealth_flows where userId = ? and wt = ? ORDER BY id DESC limit 1", new Object[]{userId, wealthType.ordinal()}, Long.class);
            return null == result ? 0L : result;
        } catch (EmptyResultDataAccessException e) {
            return 0;
        }
    }

    public long getTotalGet(long userId, WealthType wealthType) {
        try {
            Long result = getJdbcTemplate().queryForObject("select sum(offset) from user_wealth_flows where userId = ? and wt = ? and offset > 0", new Object[]{userId, wealthType.ordinal()}, Long.class);
            return null == result ? 0L : result;
        } catch (EmptyResultDataAccessException e) {
            return 0;
        }
    }

    public List<Map<String, Object>> getTableBySql(String sql, Object[] objects) {
        try {
            return getJdbcTemplate().queryForList(sql, objects);
        } catch (Exception e) {
            return null;
        }
    }

    public <T extends DomainObject> List<T> getAllObjectsByHql(final String hql, final List<Object> params) {
        return getHibernateTemplate().execute((HibernateCallback<List<T>>) session -> {
            final org.hibernate.Query query = session.createQuery(hql);
            if (null != params) {
                for (int i = 0; i < params.size(); i++) {
                    query.setParameter(i, params.get(i));
                }
            }
            query.setCacheable(true);
            return query.list();
        });
    }

    public BigDecimal getCountOfByHql(final String hql, final List<Object> params) {
        return getHibernateTemplate().execute(session -> {
            org.hibernate.Query query = session.createQuery("select count(*) " + hql);
            if (null != params) {
                for (int i = 0; i < params.size(); i++) {
                    query.setParameter(i, params.get(i));
                }
            }
            return BigDecimal.valueOf((Long) query.uniqueResult());
        });
    }

    public BigDecimal getCountOfByHql(final String hql, final HashMap<String, Object> params) {
        return getHibernateTemplate().execute(session -> {
            org.hibernate.Query query = session.createQuery("select count(*) " + hql);
            if (null != params) {
                for (String key : params.keySet()) {
                    final Object value = params.get(key);
                    if (value instanceof Collection) {
                        query.setParameterList(key, (Collection) value);
                    } else {
                        query.setParameter(key, value);
                    }
                }
            }
            return BigDecimal.valueOf((Long) query.uniqueResult());
        });
    }

    public BigDecimal getSumOfByHql(final String hql, final String filed, final List<Object> params) {
        return getHibernateTemplate().execute(session -> {
            org.hibernate.Query query = session.createQuery("select sum(" + filed + ") " + hql);
            if (null != params) {
                for (int i = 0; i < params.size(); i++) {
                    query.setParameter(i, params.get(i));
                }
            }
            final Object result = query.uniqueResult();
            return null == result ? BigDecimal.ZERO : BigDecimal.valueOf((Long) result);
        });
    }

    public <T> List<T> getAllObjectsByHql(final String hql, final List<Object> params, final int start, final int limit) {
        return getHibernateTemplate().execute((HibernateCallback<List<T>>) session -> {
            final org.hibernate.Query query = session.createQuery(hql);
            if (null != params) {
                for (int i = 0; i < params.size(); i++) {
                    query.setParameter(i, params.get(i));
                }
            }
            query.setFirstResult(start);
            query.setMaxResults(limit);
            query.setCacheable(true);
            return query.list();
        });
    }

    public BigDecimal getSumOfByHql(final String hql, final String filed, final HashMap<String, Object> params) {
        return getHibernateTemplate().execute(session -> {
            org.hibernate.Query query = session.createQuery("select sum(" + filed + ") " + hql);
            if (null != params) {
                for (String key : params.keySet()) {
                    final Object value = params.get(key);
                    if (value instanceof Collection) {
                        query.setParameterList(key, (Collection) value);
                    } else {
                        query.setParameter(key, value);
                    }
                }
            }
            final Object result = query.uniqueResult();
            return null == result ? BigDecimal.ZERO : BigDecimal.valueOf((Long) result);
        });
    }

    public <T> List<T> getAllObjectsByHql(final String hql, final HashMap<String, Object> params, final int start, final int limit) {
        return getHibernateTemplate().execute((HibernateCallback<List<T>>) session -> {
            final org.hibernate.Query query = session.createQuery(hql);
            if (null != params) {
                for (String key : params.keySet()) {
                    final Object value = params.get(key);
                    if (value instanceof Collection) {
                        query.setParameterList(key, (Collection) value);
                    } else {
                        query.setParameter(key, value);
                    }
                }
            }
            query.setFirstResult(start);
            query.setMaxResults(limit);
            query.setCacheable(true);
            return query.list();
        });
    }

    public WealthFlow getLastFlow(long userId, WealthType wealthType) {
        return getHibernateTemplate().execute(session -> {
            Query query = session.createQuery("from WealthFlow where userId = :userId and wealthType = :wealthType order by id desc");
            query.setParameter("userId", userId);
            query.setParameter("wealthType", wealthType);
            query.setMaxResults(1);
            List list = query.list();
            return list.isEmpty() ? null : (WealthFlow) list.get(0);
        });
    }

    public Bank getBank(long userId, String number) {
        return getHibernateTemplate().execute(session -> {
            final Query query = session.createQuery("from Bank where userId = :userId and number = :number");
            query.setParameter("userId", userId);
            query.setParameter("number", number);
            final List list = query.list();
            return list.isEmpty() ? null : (Bank) list.get(0);
        });
    }

}
